package butines.game.lastcolony.entities;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import butines.core.GameImage;

public class EntityClass {

    private EntityAction action;
    private EntityOrders.Type ordersType;
    
    private int direction;
    private boolean selectable;

    private String name;
    private int pixelWidth;
    private int pixelHeight;
    private int pixelOffsetX;
    private int pixelOffsetY;
    private int baseWidth;
    private int baseHeight;

    private int[][] buildableGrid;
    private int[][] passableGrid;
    private int sight;
    private int hitPoints;
    private int cost;

    private GameImage spriteSheet;
    private List<EntitySprite> spriteArray;
    private int spriteCount = 0;
    
    private boolean canAttack;
    private boolean canAttackLand;
    private boolean canAttackAir;
    private WeaponType weaponType;
    
    private int radius;
    private int speed;
    private int turnSpeed;
    
    private int pixelShadowHeight;
    
    private int directions;
    
    public int getDirections() {
        return directions;
    }

    public void setDirections(int directions) {
        this.directions = directions;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public boolean isSelectable() {
        return selectable;
    }

    public void setSelectable(boolean selectable) {
        this.selectable = selectable;
    }

    public EntityAction getAction() {
        return action;
    }

    public void setAction(EntityAction action) {
        this.action = action;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPixelWidth() {
        return pixelWidth;
    }

    public void setPixelWidth(int pixelWidth) {
        this.pixelWidth = pixelWidth;
    }

    public int getPixelHeight() {
        return pixelHeight;
    }

    public void setPixelHeight(int pixelHeight) {
        this.pixelHeight = pixelHeight;
    }

    public int getPixelOffsetX() {
        return pixelOffsetX;
    }

    public void setPixelOffsetX(int pixelOffsetX) {
        this.pixelOffsetX = pixelOffsetX;
    }

    public int getPixelOffsetY() {
        return pixelOffsetY;
    }

    public void setPixelOffsetY(int pixelOffsetY) {
        this.pixelOffsetY = pixelOffsetY;
    }

    public int getBaseWidth() {
        return baseWidth;
    }

    public void setBaseWidth(int baseWidth) {
        this.baseWidth = baseWidth;
    }

    public int getBaseHeight() {
        return baseHeight;
    }

    public void setBaseHeight(int baseHeight) {
        this.baseHeight = baseHeight;
    }

    public int[][] getBuildableGrid() {
        return buildableGrid;
    }

    public void setBuildableGrid(int[][] buildableGrid) {
        this.buildableGrid = buildableGrid;
    }

    public int[][] getPassableGrid() {
        return passableGrid;
    }

    public void setPassableGrid(int[][] passableGrid) {
        this.passableGrid = passableGrid;
    }

    public int getSight() {
        return sight;
    }

    public void setSight(int sight) {
        this.sight = sight;
    }

    public int getHitPoints() {
        return hitPoints;
    }

    public void setHitPoints(int hitPoints) {
        this.hitPoints = hitPoints;
    }

    public int getCost() {
        return cost;
    }

    public void setCost(int cost) {
        this.cost = cost;
    }

    public GameImage getSpriteSheet() {
        return spriteSheet;
    }

    public void setSpriteSheet(GameImage spriteSheet) {
        this.spriteSheet = spriteSheet;
    }

    public List<EntitySprite> getSpriteArray() {
        if (spriteArray == null) {
            spriteArray = new ArrayList<EntitySprite>();
        }
        return spriteArray;
    }

    public void setSpriteArray(List<EntitySprite> spriteArray) {
        this.spriteArray = spriteArray;
    }

    public int getSpriteCount() {
        return spriteCount;
    }

    public void setSpriteCount(int spriteCount) {
        this.spriteCount = spriteCount;
    }
    
    public void addEntitySprite(EntitySprite sprite) {
        getSpriteArray().add(sprite);
    }
    
    public void addEntitySprite(String name, int count) {
        EntitySprite sprite = new EntitySprite();
        sprite.setName(name);
        sprite.setCount(count);
        sprite.setOffset(spriteCount);
        
        addEntitySprite(sprite);
        
        spriteCount += count;
    }
    
    public void addEntitySprite(String name, int count, int directions) {
        for (int i = 0; i < directions; i++) {
            EntitySprite sprite = new EntitySprite();
            sprite.setName(name + '-' + i);
            sprite.setCount(count);
            sprite.setOffset(spriteCount);
            
            addEntitySprite(sprite);
            
            spriteCount += count;
        }
    }
    
    public EntitySprite getEntitySprite(String name) {
        EntitySprite sprite = null;
        for (int i = 0; i < getSpriteArray().size(); i++) {
            sprite = getSpriteArray().get(i);
            if (name.equals(sprite.getName())) {
                return sprite;
            }
        }
        return null;
    }
    
    private static final Map<Class<?>, Class<?>> primitiveTypes;
    static {
        primitiveTypes = new HashMap<Class<?>, Class<?>>();
        primitiveTypes.put(Byte.class, byte.class);
        primitiveTypes.put(Short.class, short.class);
        primitiveTypes.put(Integer.class, int.class);
        primitiveTypes.put(Long.class, long.class);
        primitiveTypes.put(Float.class, float.class);
        primitiveTypes.put(Double.class, double.class);
        primitiveTypes.put(Character.class, char.class);
        primitiveTypes.put(Boolean.class, boolean.class);
    }
    
    @SuppressWarnings("unchecked")
    public <T extends Entity> T create(Class<T> clazz, Object ... values) {
        try  {
            Entity entity = clazz.newInstance();
            entity.setEntityClass(this);
            entity.setAction(getAction());
            entity.setOrders(new EntityOrders(getOrdersType()));
            entity.setLife(getHitPoints());
            
            for (int i = 0; i < values.length; i += 2) {
                String propertyName = values[i].toString();
                Object propertyValue = values[i + 1];
                
                StringBuilder name = new StringBuilder("set");
                name.append(Character.toUpperCase(propertyName.charAt(0)));
                name.append(propertyName.substring(1));
                                
                Method method = null;
                if (primitiveTypes.containsKey(propertyValue.getClass())) {
                    try {
                        Class<?> type = primitiveTypes.get(propertyValue.getClass());
                        method = clazz.getMethod(name.toString(), type);
                    } catch (NoSuchMethodException e) {
                        method = clazz.getMethod(name.toString(), propertyValue.getClass());
                    }
                } else {
                    method = clazz.getMethod(name.toString(), propertyValue.getClass());
                }
                method.invoke(entity, propertyValue);
            }
            
            return (T) entity;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean isCanAttack() {
        return canAttack;
    }

    public void setCanAttack(boolean canAttack) {
        this.canAttack = canAttack;
    }

    public boolean isCanAttackLand() {
        return canAttackLand;
    }

    public void setCanAttackLand(boolean canAttackLand) {
        this.canAttackLand = canAttackLand;
    }

    public boolean isCanAttackAir() {
        return canAttackAir;
    }

    public void setCanAttackAir(boolean canAttackAir) {
        this.canAttackAir = canAttackAir;
    }

    public WeaponType getWeaponType() {
        return weaponType;
    }

    public void setWeaponType(WeaponType weaponType) {
        this.weaponType = weaponType;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public int getTurnSpeed() {
        return turnSpeed;
    }

    public void setTurnSpeed(int turnSpeed) {
        this.turnSpeed = turnSpeed;
    }

    public int getPixelShadowHeight() {
        return pixelShadowHeight;
    }

    public void setPixelShadowHeight(int pixelShadowHeight) {
        this.pixelShadowHeight = pixelShadowHeight;
    }

    public EntityOrders.Type getOrdersType() {
        return ordersType;
    }

    public void setOrdersType(EntityOrders.Type ordersType) {
        this.ordersType = ordersType;
    }

}
